package ddz.core;

import java.io.Serializable;
import java.util.Arrays;
import java.util.function.Consumer;

public class Cards implements Serializable {

    /**
     * 获取牌的花色
     *
     * @param card 600为大王，500为小王，其它为 花色类型 * 100 + 牌点
     * @return 花色
     */
    public final static int getSuit(int card) {
        return card / 100;
    }

    /**
     * 获取牌点，大小王的牌点为0
     *
     * @param card 2为大王，1为小王，其它为 花色类型 * 100 + 牌点
     * @return 牌点
     */
    public final static int getPoint(int card) {
        return card % 100;
    }

    /**
     * 获取牌簇对应手牌中的位置索引
     *
     * @param cardOrPoint 牌数字表示或者牌点
     * @return 牌簇pile的位置索引
     */
    public final static int getIndex(int cardOrPoint) {
        int point;
        if (cardOrPoint < 100) {
            point = cardOrPoint;
        } else {
            point = cardOrPoint % 100;
        }
        if (point == 2) return 12;
        if (point == 0) return 13;
        return point - 3;
    }

    /**
     * 根据牌簇的数组下标索引获得其对应的牌点
     *
     * @param pileIndex 牌簇的数组下标索引
     * @return 牌簇的数组下标索引对应的牌点
     */
    public final static int getPointByIndex(int pileIndex) {
        if (pileIndex >= 0 && pileIndex <= 11) {
            return pileIndex + 3;
        }
        if (pileIndex == 12) {
            return 2;
        }
        if (pileIndex == 13) {
            return 0;
        }
        return -1;
    }

    /**
     * 组装癞子替代牌
     *
     * @param foreCard  前三位的牌
     * @param laiziCard 后三位的癞子牌
     * @return 六位数的癞子牌
     */
    public final static int assembleSixCard(int foreCard, int laiziCard) {
        return foreCard * 1000 + laiziCard;
    }

    /**
     * 分离出被癞子替代的牌
     *
     * @param card 六位数癞子牌
     * @return 被癞子替代的牌
     */
    public final static int spliteForeCard(int card) {
        if (card > 1000) return card / 1000;
        return card;
    }

    /**
     * 分离出真实的癞子牌
     *
     * @param card 六位数癞子牌
     * @return 真实的三位数癞子牌
     */
    public final static int spliteBackCard(int card) {
        if (card > 1000) return card % 1000;
        return 0;
    }

    /**
     * 比较card1是否大于card2
     *
     * @param card1
     * @param card2
     * @return -1表示card1小于card2， 0表示相等， 1表示card1大于card2
     */
    public final static int compare(int card1, int card2) {
        int pot1 = getPoint(card1);
        int pot2 = getPoint(card2);
        //都不为大小王
        if (pot1 != 0 && pot2 != 0) {
            if (pot1 == pot2) return 0;
            if (pot1 == 2) return 1;
            if (pot1 >= 3 && pot2 >= 3) {
                if (pot1 > pot2) return 1;
                if (pot1 == pot2) return 0;
            }
            return -1;
        }
        //都为大小王
        if (pot1 == pot2) {
            int suit1 = getSuit(card1);
            int suit2 = getSuit(card2);
            if (suit1 > suit2) return 1;
            if (suit1 == suit2) return 0;
            if (suit1 < suit2) return -1;
        }
        //一方为大小王
        if (pot1 == 0) return 1;
        return -1;
    }

    private final Pile[] array; //聚合牌数据数组
    private int pileCount = 0; //有效的聚合牌数据数量
    private int cardTotals = 0; //所有牌张数
    private int minValidIndex = 1000; //聚合牌数据数组中有效的最小索引，最小牌点的牌
    private int maxValidIndex = -1; //聚合牌数据数组中有效的最大索引，最大牌点的牌

    /**
     * 构造方法
     */
    public Cards() {
        this.array = new Pile[14];
    }

    /**
     * 吞并，即将另一个对象的牌合并到本对象中
     *
     * @param other 被吞并的对象
     */
    public final void annex(Cards other) {
        if (other.minValidIndex < this.minValidIndex) {
            this.minValidIndex = other.minValidIndex;
        }
        if (other.maxValidIndex > this.maxValidIndex) {
            this.maxValidIndex = other.maxValidIndex;
        }
        for (int i = other.getMinValidIndex(); i <= other.getMaxValidIndex(); i++) {
            Pile thisPile = this.getPileByIndex(i);
            Pile otherPile = other.getPileByIndex(i);
            thisPile.annex(otherPile);
            if (thisPile.count <= 0 && otherPile.count > 0) {
                this.pileCount++;
            }
        }
    }

    /**
     * 添加牌组
     *
     * @param pile 聚合牌组对象
     * @return true添加成功
     */
    public final boolean insertPile(Pile pile) {
        if (pile == null) return false;
        if (pile.getCount() == 0) return false;
        int pileIndex = getIndex(pile.getPoint());
        removePile(pileIndex);
        array[pileIndex] = pile;
        pileCount++;
        cardTotals += pile.getCount();
        if (pileIndex < minValidIndex) {
            minValidIndex = pileIndex;
        }
        if (pileIndex > maxValidIndex) {
            maxValidIndex = pileIndex;
        }
        return true;
    }

    /**
     * 修正minValidIndex 和 maxValidIndex
     *
     * @param pileIndex 牌簇索引
     * @return 牌簇索引
     */
    int correct(int pileIndex) {
        if (pileIndex == minValidIndex) {
            minValidIndex = 1000;
            for (int i = pileIndex + 1; i < array.length; i++) {
                if (array[i] != null && array[i].getCount() > 0) {
                    minValidIndex = i;
                    break;
                }
            }
        }
        if (pileIndex == maxValidIndex) {
            int start = minValidIndex;
            if (start >= array.length) start = 0;
            maxValidIndex = -1;
            for (int i = pileIndex - 1; i >= start; i--) {
                if (array[i] != null && array[i].getCount() > 0) {
                    maxValidIndex = i;
                    break;
                }
            }
        }
        return pileIndex;
    }

    /**
     * 删除牌组
     *
     * @param pileIndex 牌组在数组中的下标，亦可表示牌点
     * @return Pile 删除成功
     */
    public final Pile removePile(int pileIndex) {
        Pile pile = array[pileIndex];
        if (pile == null) return null;
        if (pile.getCount() == 0) return pile;
        cardTotals -= pile.getCount();
        pileCount--;
        correct(pileIndex);
        array[pileIndex] = null;
        return pile;
    }

    public final boolean hasCard(int card, int num) {
        int point = getPoint(card);
        if (point == 1) {
            throw new Error("为1的牌点所指示的索引为保留位");
        }
        int pileIndex = getIndex(card);
        Pile pile = getPileByIndex(pileIndex);
        if (pile == null) return false;
        int suitIndex = pile.suitIndex(card);
        return pile.suits[suitIndex] >= num;
    }

    public final boolean hasCard(int card) {
        return hasCard(card, 1);
    }

    /**
     * 添加一张牌
     *
     * @param card 添加的牌
     * @return true添加成功
     */
    public final boolean addCard(int card) {
        int point = getPoint(card);
        if (point == 1) {
            throw new Error("为1的牌点所指示的索引为保留位");
        }
        int pileIndex = getIndex(point);
        if (array[pileIndex] == null) {
            array[pileIndex] = new Pile();
        }
        Pile pile = array[pileIndex];
        boolean result = pile.addCard(card);
        if (!result) {
            return false;
        }
        cardTotals++;
        if (pile.count == 1) {
            pileCount++;
        }
        if (pileIndex < minValidIndex) {
            minValidIndex = pileIndex;
        }
        if (pileIndex > maxValidIndex) {
            maxValidIndex = pileIndex;
        }
        return true;
    }

    /**
     * 添加牌，添加的任意一张失败则全部失败
     *
     * @param cards 添加的牌列表
     * @return true全部添加成功
     */
    public final boolean addCard(int... cards) {
        int[] addedCards = new int[cards.length];
        boolean flag = true;
        for (int i = 0; i < cards.length; i++) {
            int card = cards[i];
            boolean bool = addCard(card);
            if (!bool) {
                flag = false;
                break;
            }
            addedCards[i] = card;
        }
        if (!flag) {
            for (int card : addedCards) {
                if (card > 0) {
                    removeCard(card);
                }
            }
        }
        return flag;
    }

    /**
     * 移除一张牌
     *
     * @param card 移除的牌
     * @return true移除成功
     */
    public final boolean removeCard(int card) {
        int point = getPoint(card);
        if (point == 1) {
            throw new Error("为1的牌点所指示的索引为保留位");
        }
        int pileIndex = getIndex(point);
        Pile pile = array[pileIndex];
        if (pile == null) return false;
        if (pile.count <= 0) return false;
        boolean result = pile.removeCard(card);
        if (!result) return false;
        cardTotals--;
        if (pile.count == 0) {
            pileCount--;
            correct(pileIndex);
            array[pileIndex] = null;
        }
        return true;
    }

    /**
     * 移除牌，一张移除失败则全部失败
     *
     * @param cards 移除的牌列表
     * @return true移除成功
     */
    public final boolean removeCard(int... cards) {
        int[] removedCards = new int[cards.length];
        boolean flag = true;
        for (int i = 0; i < cards.length; i++) {
            int card = cards[i];
            boolean bool = removeCard(card);
            if (!bool) {
                flag = false;
                break;
            }
            removedCards[i] = card;
        }
        if (!flag) {
            for (int card : removedCards) {
                if (card > 0) {
                    addCard(card);
                }
            }
        }
        return flag;
    }

    /**
     * 获取牌总量
     *
     * @return
     */
    public final int getCardCount() {
        return cardTotals;
    }

    /**
     * 获取牌聚合组的个数
     *
     * @return
     */
    public final int getPileCount() {
        return pileCount;
    }

    /**
     * 获取聚合牌组有效的最小索引
     *
     * @return
     */
    public final int getMinValidIndex() {
        if (minValidIndex >= array.length) {
            return -1;
        }
        return minValidIndex;
    }

    /**
     * 获取聚合牌组有效的最大索引
     *
     * @return
     */
    public final int getMaxValidIndex() {
        return maxValidIndex;
    }

    /**
     * 清除数据
     */
    public final void clear() {
        this.pileCount = 0;
        this.cardTotals = 0;
        this.minValidIndex = 1000;
        this.maxValidIndex = -1;
        for (Pile pile : array) {
            if (pile != null) {
                pile.clear();
            }
        }
    }

    public final Pile getPileByIndex(int index) {
        if (index < 0) {
            return null;
        }
        if (index >= array.length) {
            return null;
        }
        return array[index];
    }

    public final Pile getPileByCard(int card) {
        int point = Cards.getPoint(card);
        if (point == 1) {
            throw new Error("为1的牌点所指示的索引为保留位");
        }
        int pileIndex = getIndex(card);
        return array[pileIndex];
    }

    public final Pile getJokerPile() {
        int jokerIdx = Cards.getIndex(0);
        return this.getPileByIndex(jokerIdx);
    }

    /**
     * 获取牌数据，根据牌点从小到大
     *
     * @return 所有的牌
     */
    public final int[] getCards() {
        if (cardTotals <= 0) {
            return null;
        }
        int[] cards = new int[cardTotals];
        int idx = 0;
        for (int i = this.array.length - 1; i >= 0; i--) {
            Pile pile = array[i];
            if (pile != null && pile.getCount() > 0) {
                int[] pileCards = pile.getCards();
                if (pileCards != null && pileCards.length > 0) {
                    System.arraycopy(pileCards, 0, cards, idx, pileCards.length);
                    idx += pileCards.length;
                }
            }
        }
        return cards;
    }

    public final int[] getDesCards() {
        if (cardTotals <= 0) {
            return null;
        }
        int[] cards = new int[cardTotals];
        int idx = 0;
        for (int i = this.getMinValidIndex(); i <= this.getMaxValidIndex(); i++) {
            Pile pile = array[i];
            if (pile != null && pile.getCount() > 0) {
                int[] pileCards = pile.getCards();
                System.arraycopy(pileCards, 0, cards, idx, pileCards.length);
                idx += pileCards.length;
            }
        }
        return cards;
    }

    /**
     * 获取降序规则的牌组列表 </br>
     * 比如： 王、2、A、K、Q、J、10、9、……
     *
     * @return
     */
    public Pile[] getDesPiles() {
        Pile[] arr = new Pile[array.length];
        for (int i = this.array.length - 1; i >= 0; i--) {
            Pile pile = array[i];
            if (pile != null && pile.getCount() > 0) {
                arr[i] = pile.clone();
            }
        }
        return arr;
    }

    public void forEach(Consumer<Pile> action) {
        int count = this.maxValidIndex - this.minValidIndex + 1;
        if (count <= 0) {
            return;
        }
        for (int i = minValidIndex; i <= maxValidIndex; i++) {
            Pile o_pile = array[i];
            if (o_pile != null) {
                action.accept(o_pile);
            }
        }
    }

    @Override
    public Cards clone() {
        Cards copy = new Cards();
        this.forEach((Pile pile) -> {
            pile.forEach((Integer card) -> {
                copy.addCard(card);
            });
        });
        return copy;
    }

    @Override
    public String toString() {
        return "HandCards{" +
                "array=" + Arrays.toString(array) +
                ", pileCount=" + pileCount +
                ", cardTotals=" + cardTotals +
                ", minValidIndex=" + minValidIndex +
                ", maxValidIndex=" + maxValidIndex +
                '}';
    }

}